/* A COMPLETER - Pef, 2 mai
	Di-Costanzo Jonathan
	Fauché Pierre-Élie
	3TC 3
*/

#include <stdlib.h>
#include <string.h>

#include "p2p_common.h"
#include "p2p_addr.h"
#include "p2p_msg.h"
#include "p2p_topology.h"

/***************************************************************************
Apres réception d'un JOIN ACK
***************************************************************************/
int p2p_join_ack_received (server_params *sp, p2p_msg ack_msg) {
	
	unsigned char *ack_payload=p2p_get_payload(ack_msg);
	
	// on récupère les adresses stockées dans le payload du message JOIN_ACK
	p2p_addr left = p2p_addr_create();
	memcpy(left, ack_payload, 8);
	p2p_addr right = p2p_addr_create();
	memcpy(right, &ack_payload[8], 8);
	
	//préparation des messages link update
	p2p_msg link_msg = p2p_msg_create();
	unsigned char link_payload[12];
	
	//pour mon nouveau gauche   
	if (p2p_msg_init(link_msg, P2P_MSG_LINK_UPDATE, P2P_MSG_TTL_ONE_HOP, 
		sp->p2pMyId, left) != P2P_OK) return P2P_ERROR;
	memcpy(link_payload, sp->p2pMyId, 8);
	unsigned long int type = htonl(0x0000FFFF);
	memcpy(&link_payload[8], &type, 4);
	if (p2p_msg_init_payload(link_msg, 12, link_payload) != P2P_OK) 
		return P2P_ERROR;
	//envoi du message
	if ( p2p_addr_is_equal(sp->p2pMyId, p2p_msg_get_dst(link_msg))==0 ) {
		if (p2p_tcp_msg_send(sp, link_msg) != P2P_OK) {
			return P2P_ERROR;
		}
	}
		
	//pour son ancien droit   
	if (p2p_msg_init(link_msg, P2P_MSG_LINK_UPDATE, P2P_MSG_TTL_ONE_HOP, 
		sp->p2pMyId, right) != P2P_OK) return P2P_ERROR;
	memcpy(link_payload, sp->neighbors.left, 8);
	type = htonl(0xFFFF0000);
	memcpy(&link_payload[8], &type, 4);
	if (p2p_msg_init_payload(link_msg, 12, link_payload) != P2P_OK) 
		return P2P_ERROR;
	//envoi du message
	if ( p2p_addr_is_equal(sp->p2pMyId, p2p_msg_get_dst(link_msg))==0 ) {
		if (p2p_tcp_msg_send(sp, link_msg) != P2P_OK)
			return P2P_ERROR;
	}
	
	//pour mon ancien gauche   
	if (p2p_msg_init(link_msg, P2P_MSG_LINK_UPDATE, P2P_MSG_TTL_ONE_HOP, 
		sp->p2pMyId, sp->neighbors.left) != P2P_OK) return P2P_ERROR;
	memcpy(link_payload, right, 8);
	type = htonl(0x0000FFFF);
	memcpy(&link_payload[8], &type, 4);
	if (p2p_msg_init_payload(link_msg, 12, link_payload) != P2P_OK) 
		return P2P_ERROR;
	//envoi du message
	if ( p2p_addr_is_equal(sp->p2pMyId, p2p_msg_get_dst(link_msg))==0 ) {
		if (p2p_tcp_msg_send(sp, link_msg) != P2P_OK)
			return P2P_ERROR;
	} else {
		p2p_addr_delete(sp->neighbors.right);
		sp->neighbors.right=p2p_addr_duplicate(right);
	}
	
	//pour moi
	p2p_addr_delete(sp->neighbors.left);
	sp->neighbors.left  = left;
	
	//free
	p2p_addr_delete(right);
	p2p_msg_delete(link_msg);
	
  return(P2P_OK);
}

/***************************************************************************OK
Envoi d'un message JOIN_REQ
***************************************************************************/
int p2p_join_req (server_params *sp, p2p_addr destinataire) {
	
	//création du message à envoyer
  p2p_msg join_msg = p2p_msg_create();
  p2p_msg ack_msg = p2p_msg_create();
  
  //on remplit le message avec une longueur nulle
  if ( p2p_msg_init(join_msg, P2P_MSG_JOIN_REQ, P2P_MSG_TTL_ONE_HOP, sp->p2pMyId, destinataire) != P2P_OK ) 
    return(P2P_ERROR);
  p2p_msg_set_length(join_msg, 0);
  
  // on envoi le message
  int socket = p2p_tcp_socket_create(sp, destinataire);
  if ( p2p_tcp_msg_sendfd(sp, join_msg, socket) != P2P_OK )
    return (P2P_ERROR);
    
  //réception du message d'acquittement
  if (p2p_tcp_msg_recvfd(sp, ack_msg, socket) != P2P_OK) {
  	return (P2P_ERROR);
  }
  //on peut fermer la socket
  p2p_tcp_socket_close(sp, socket);
  
  //on traite le message d'acquittement
  if (p2p_join_ack_received(sp, ack_msg) != P2P_OK) {
  	return P2P_ERROR;
  }
  
  //on supprime le message créé et toutes les adresses
  p2p_msg_delete(join_msg);
  p2p_msg_delete(ack_msg);
  
  return(P2P_OK);
}

/***************************************************************************OK
Réception d'un message JOIN_REQ, on envoie un message JOIN_ACK
***************************************************************************/
int p2p_join_asked (server_params *sp, p2p_msg join_req_msg, int socket) {
  unsigned char ack_payload[16];
	
	VERBOSE(sp,VMRECV,"RECV join request from %s\n",p2p_addr_get_str(p2p_msg_get_src(join_req_msg)));
	
	//création du message JOIN_ACK
	p2p_msg join_ack_msg = p2p_msg_create();
	//on remplit le message avec une longueur de 16 octets
  if ( p2p_msg_init(join_ack_msg, P2P_MSG_JOIN_ACK, P2P_MSG_TTL_ONE_HOP, 
  		sp->p2pMyId, p2p_msg_get_src(
  		join_req_msg)) != P2P_OK ) 
    return(P2P_ERROR);
    
  // on remplit le ack_payload
  memcpy(ack_payload, sp->p2pMyId, 8);
  memcpy(&ack_payload[8], sp->neighbors.right, 8);
	p2p_msg_init_payload(join_ack_msg, 16, ack_payload);
	
	// envoi du message
	if ( p2p_tcp_msg_sendfd(sp, join_ack_msg, socket) != P2P_OK )
    return (P2P_ERROR);
	
	//on supprime le message créé
  p2p_msg_delete(join_ack_msg);
  
  VERBOSE(sp,VMCTNT,"join request done\n");
  
  return(P2P_OK);
}

/***************************************************************************OK
Réception d'un message LINK_UPDATE
***************************************************************************/
int p2p_update_received(server_params *sp, p2p_msg update_msg) {
	
	p2p_addr new_addr = p2p_addr_create();
	memcpy(new_addr, p2p_get_payload(update_msg), 8);
	
	//quel est le voisin à changer ?
	unsigned long int type;
	memcpy(&type,&(p2p_get_payload(update_msg)[8]), 4);
	type=ntohl(type);
	if (type==0xFFFF0000) {
		//voisin gauche
		p2p_addr_copy(sp->neighbors.left, new_addr);
	} else {
		// voisin droit	
		p2p_addr_copy(sp->neighbors.right, new_addr);
	}
	
	p2p_addr_delete(new_addr);
	
	return P2P_OK;
}

/***************************************************************************OK
Quitte le réseau p2p
***************************************************************************/
int p2p_leave (server_params *sp) {
	
	if ( (p2p_addr_is_equal(sp->p2pMyId, sp->neighbors.left)==0) || (p2p_addr_is_equal(sp->p2pMyId, sp->neighbors.right)==0) ) {
	//préparation des messages link update
	unsigned char link_payload[12];
	
	//pour voisin gauche:
	p2p_msg link_left_msg = p2p_msg_create();
	if (p2p_msg_init(link_left_msg, P2P_MSG_LINK_UPDATE, P2P_MSG_TTL_ONE_HOP, 
		sp->p2pMyId, sp->neighbors.left) != P2P_OK) return P2P_ERROR;
	memcpy(link_payload, sp->neighbors.right, 8);
	unsigned long int type = htonl(0x0000FFFF);
	memcpy(&link_payload[8], &type, 4);
	if (p2p_msg_init_payload(link_left_msg, 12, link_payload) != P2P_OK) 
		return P2P_ERROR;
		
	//pour voisin droit:
	p2p_msg link_right_msg = p2p_msg_create();
	if (p2p_msg_init(link_right_msg, P2P_MSG_LINK_UPDATE, P2P_MSG_TTL_ONE_HOP, 
		sp->p2pMyId, sp->neighbors.right) != P2P_OK) return P2P_ERROR;
	memcpy(link_payload, sp->neighbors.left, 8);
	type = htonl(0xFFFF0000);
	memcpy(&link_payload[8], &type, 4);
	if (p2p_msg_init_payload(link_right_msg, 12, link_payload) != P2P_OK) 
		return P2P_ERROR;
		
	//envoi des messages
	if ( p2p_addr_is_equal(sp->p2pMyId, p2p_msg_get_dst(link_left_msg))==0 )
		if (p2p_tcp_msg_send(sp, link_left_msg) != P2P_OK)
			return P2P_ERROR;
	if ( p2p_addr_is_equal(sp->p2pMyId, p2p_msg_get_dst(link_right_msg))==0 )
		if (p2p_tcp_msg_send(sp, link_right_msg) != P2P_OK)
			return P2P_ERROR;
	/*if (p2p_tcp_msg_send(sp, link_left_msg) != P2P_OK)
		return P2P_ERROR;
	if (p2p_tcp_msg_send(sp, link_right_msg) != P2P_OK)
		return P2P_ERROR;*/
    
  //on supprime les messages et les structures créés
  p2p_msg_delete(link_left_msg);
  p2p_msg_delete(link_right_msg);

	//on met nos voisins sur nous-même
	p2p_addr_copy(sp->neighbors.left, sp->p2pMyId);
	p2p_addr_copy(sp->neighbors.right, sp->p2pMyId);
	
	}
	return(P2P_OK);
}
